package com.mach.platform.sprider.dev;

import com.mach.platform.sprider.dev.s7.SimaticClient;
import com.mach.platform.util.U;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.concurrent.LinkedBlockingQueue;

public class TcpWorker implements  Runnable{

    private String ip = "127.0.0.1";

    private Integer port = 80;

    private Socket sk = null;

    private InputStream in = null;
    private OutputStream out = null;
    private boolean isAct = false;
    private int soTimeOutValue = 500;

    private SimaticClient client = null;

    public TcpWorker(String ip, Integer port){
        this.ip = ip;
        this.port = port;
    }

    public int getSoTimeOutValue() {
        return soTimeOutValue;
    }

    public void setSoTimeOutValue(int soTimeOutValue) {
        this.soTimeOutValue = soTimeOutValue;
    }

    synchronized public void send(byte[] buf) throws IOException {

        try {
            con();
            this.out.write(buf);
            this.out.flush();
        } catch (IOException e) {
            reSet();
            throw e;
        }

    }
    synchronized public byte[] read4TimeOut() throws IOException {
        byte[] result = null;
        try {
            con();
            ByteArrayOutputStream bout=new ByteArrayOutputStream();
            int b=0;
            try {
                while( (b=this.in.read()) != -1 )
                {
                    bout.write(b);
                }
            } catch (SocketTimeoutException e) {
                //System.out.println("devId:" + this.getDevAddr() + " readTimeOut finish");//debug log
            }
            bout.close();

            result = bout.toByteArray();
        } catch (IOException e) {
            reSet();
            throw e;
        }
        return result;
    }

    public byte[] command(byte[] buff) throws IOException {
//        send(buff);
//        return this.read4TimeOut();

        return commandHasProi(buff, 0);   //  buff  指令码
    }

    public byte[] commandHasProi(byte[] buff,Integer proi) throws IOException {
        byte[] result = null;
        try {
            Pkg pkg = comit(buff, proi);
//            result = this.read4TimeOut();

            while(!pkg.hasResp){
                Thread.sleep(5);
            }
            result = pkg.respBuf;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return result;
    }

    private LinkedBlockingQueue<Pkg> queue0  = new LinkedBlockingQueue<Pkg>(1000);
    private LinkedBlockingQueue<Pkg> queue1  = new LinkedBlockingQueue<Pkg>(1000);

    private Pkg comit(byte[] buff,Integer proi) throws InterruptedException {
        Pkg pkg = new Pkg();
//        pkg.id = System.currentTimeMillis();
        pkg.hasResp = false;
        pkg.sendBuf = buff;

        if(proi == 1){
            queue1.put(pkg);
        }else{
            queue0.put(pkg);
        }

        this.act();

        return pkg;
    }

    private int thCnt = 0;
    synchronized private void act() {
        if(this.isAct){

        }else{
            U.cachedThreadPool.execute(this);
            thCnt++;
        }
    }

    public void con() throws IOException {
        if(sk == null){
            sk = new Socket(this.ip, this.port);
            sk.setSoTimeout(this.soTimeOutValue);
        }

        this.in = sk.getInputStream();

        this.out = sk.getOutputStream();
    }

    public void reCon() throws IOException {
        this.reSet();
        this.con();
    }

    public  void reSet() {
        if(this.sk != null){
            this.close();
        }
        this.sk = null;
    }


    public void close(){
        try {
            this.sk.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {

        this.isAct = true;

        try {
            long startTime = System.currentTimeMillis();
            while (true){

                Pkg pkg = null;
                try {
                    if(!this.queue1.isEmpty()){
                         pkg = this.queue1.take();
                    }else {
                        if(!this.queue0.isEmpty()){
                            pkg = this.queue0.take();
                        }
                    }

                    if(pkg == null){
                        long endTime = System.currentTimeMillis();
                        long dt = endTime - startTime;
                        if(dt > 100){
//                            System.out.println("task time:" + dt);
                        }
                        Thread.sleep(50);
                        startTime = System.currentTimeMillis();
                        continue;
                    }

                    this.send(pkg.sendBuf);
                    pkg.respBuf = this.read4TimeOut();


                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                    System.out.println("excep this.ip="+this.ip+ " port="+ this.port + " tid=" + Thread.currentThread().getName()
                            + " thCnt=" + this.thCnt);
                }
                pkg.hasResp = true;

            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            this.isAct = false;
        }
    }

    public SimaticClient getSimaticClient(){
        if(this.client == null){
            client = new SimaticClient(this.ip, this.port);
        }
        return client;
    }
}
